package com.xin.di.uav.applet.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.squareup.okhttp.HttpUrl;
import com.xin.di.uav.applet.service.OrderService;
import com.xin.di.uav.applet.service.WechatPayService;
import com.xin.di.uav.applet.utils.ToolUtil;
import com.xin.di.uav.common.awt.JwtUtil;
import com.xin.di.uav.common.enums.ServicePrefixEnum;
import com.xin.di.uav.common.utils.RedisPrefixKey;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import com.ejlchina.okhttps.OkHttps;

import java.io.IOException;
import java.math.BigDecimal;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.*;

@Slf4j
@Service
public class WechatPayServiceImpl implements WechatPayService {

    @Autowired
    @Qualifier("redisTemplate")
    private RedisTemplate redisTemplate;

    @Autowired
    private OrderService orderService;

    //appKeyV3 5Z9A4Gr12Y549Xd73E2Tr45T3EBV678p
    //v2 Yu783IO98gfv23nf78EC8945Ms09Oi87
    private static String PAY_URL = "https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi";

    @Value("${wx.key-path:D:\\1727006153_20250909_cert\\apiclient_key.pem}")
    private String KEY_PATH;
    private static String APP_ID = "wx69c496e13df0fb51";
    private static String MCH_ID = "1727006153";

    private static String MERCHANT_SERIAL_NUMBER = "1a0d73e8bd8f023d56afee6eb6d028f7a9068b97";

    @Override
    public Map<String, String> wechatPay(BigDecimal amount, String outTradeNo, Map<String, String> attach, String description, String loginCode) {
        String attachStr = "";
        if (attach != null && !attach.isEmpty()) {
            attachStr = JSONObject.toJSONString(attach);
        }

        Map<String, Object> amountMap = new HashMap<>();
//        amountMap.put("total", 1);//单位分
        amountMap.put("total", amount.multiply(BigDecimal.valueOf(100)).intValue());//单位分
        amountMap.put("currency", "CNY");
        String userId = JwtUtil.getUserId();
        String openid = (String) redisTemplate.opsForValue().get(ServicePrefixEnum.RESCUE + RedisPrefixKey.APPLET_OPENID + userId);
        if (Objects.isNull(openid)) {
            openid = orderService.getNewOpenId(loginCode);
        }
        SortedMap<String, Object> parameters = new TreeMap<>();

        parameters.put("appid", APP_ID);
        parameters.put("mchid", MCH_ID);
        parameters.put("description", description);
        parameters.put("payer", JSONObject.parseObject("{\n" +
                "  \"openid\": \"" + openid + "\"\n" +
                "}"));
        parameters.put("out_trade_no", outTradeNo); //订单id
        parameters.put("amount", amountMap);
        parameters.put("attach", attachStr);
        parameters.put("notify_url", "https://cindyorder.com/uocs-rescue/" + "home/wx-pay/notify");

        String token = getToken("POST", PAY_URL, JSONObject.toJSONString(parameters));
        JSONObject resp = OkHttps.sync(PAY_URL).addHeader("Authorization", token).addHeader("Accept", "application/json").setBodyPara(parameters).bodyType(MediaType.APPLICATION_JSON_VALUE).post().getBody().toBean(JSONObject.class);

        log.warn("resp:{}", resp);


        try {
            //处理成功
            String prepayId = resp.getString("prepay_id");
            //生成代签名的支付信息
            String nonceStr = ToolUtil.randomStr(12);
            String timestamp = String.valueOf(System.currentTimeMillis() / 1000);

            String packageVal = "prepay_id=" + prepayId;
            String message =
                    APP_ID + "\n" + timestamp + "\n" + nonceStr + "\n" + packageVal + "\n";
            java.security.Signature signer = java.security.Signature.getInstance("SHA256withRSA");
            PrivateKey priKey = getPrivateKey(KEY_PATH);
            signer.initSign(priKey);
            signer.update(message.getBytes("UTF-8"));
            byte[] signBytes = signer.sign();

            // 3. 生成支付签名
            Map<String, String> params = new HashMap<>();
            params.put("appId", APP_ID);
            params.put("timeStamp", timestamp);
            params.put("nonceStr", nonceStr);
            params.put("package", "prepay_id=" + prepayId);
            params.put("signType", "MD5");
            // 4. 返回支付参数
            params.put("paySign", java.util.Base64.getEncoder().encodeToString(signBytes));
            return params;
        } catch (Exception e) {
            log.error("wechat_pay ERR", e);
        }


        return null;
    }

    /**
     * 生成MD5签名
     *
     * @param params 请求参数Map
     * @param apiKey 商户密钥
     * @return 32位大写MD5签名
     */
    public static String generateSign(Map<String, String> params, String apiKey) {
        try {
            // 1. 参数按key字典序排序
            Map<String, String> sortedParams = new TreeMap<>(params);

            // 2. 拼接键值对(key=value)
            StringBuilder sb = new StringBuilder();
            for (Map.Entry<String, String> entry : sortedParams.entrySet()) {
                if (entry.getValue() != null && entry.getValue().length() > 0) {
                    sb.append(entry.getKey()).append("=").append(entry.getValue()).append("&");
                }
            }

            // 3. 拼接API密钥
            sb.append("key=").append(apiKey);

            // 4. MD5加密并转大写
            MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] bytes = md.digest(sb.toString().getBytes("UTF-8"));

            // 5. 字节数组转16进制字符串
            StringBuilder sign = new StringBuilder();
            for (byte b : bytes) {
                String hex = Integer.toHexString(b & 0xFF);
                if (hex.length() == 1) {
                    sign.append("0");
                }
                sign.append(hex);
            }

            return sign.toString().toUpperCase();
        } catch (Exception e) {
            throw new RuntimeException("生成签名失败", e);
        }
    }

    private String getToken(String method, String urlStr, String body) {
        HttpUrl url = HttpUrl.parse(urlStr);
        String nonceStr = ToolUtil.randomStr(16);
        long timestamp = System.currentTimeMillis() / 1000;
        String message = buildMessage(method, url, timestamp, nonceStr, body);

        String signature = null;
        try {
            signature = sign(message.getBytes("utf-8"));
        } catch (Exception e) {
            log.error("签名错误：[{}]", e.getMessage());
            e.printStackTrace();
        }

        return "WECHATPAY2-SHA256-RSA2048 mchid=\"" + MCH_ID + "\","
                + "nonce_str=\"" + nonceStr + "\","
                + "timestamp=\"" + timestamp + "\","
                + "serial_no=\"" + MERCHANT_SERIAL_NUMBER + "\","
                + "signature=\"" + signature + "\"";
    }

    private String sign(byte[] message) throws Exception {

        PrivateKey priKey = getPrivateKey(KEY_PATH);

        Signature sign = Signature.getInstance("SHA256withRSA");
        sign.initSign(priKey);
        sign.update(message);

        return Base64.getEncoder().encodeToString(sign.sign());
    }

    public static PrivateKey getPrivateKey(String filename) throws IOException {

        String content = new String(Files.readAllBytes(Paths.get(filename)), "utf-8");
        try {
            String privateKey = content.replace("-----BEGIN PRIVATE KEY-----", "")
                    .replace("-----END PRIVATE KEY-----", "")
                    .replaceAll("\\s+", "");

            KeyFactory kf = KeyFactory.getInstance("RSA");
            return kf.generatePrivate(
                    new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKey)));
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("当前Java环境不支持RSA", e);
        } catch (InvalidKeySpecException e) {
            throw new RuntimeException("无效的密钥格式");
        }
    }


    private static String buildMessage(String method, HttpUrl url, long timestamp, String nonceStr, String body) {
        String canonicalUrl = url.encodedPath();
        if (url.encodedQuery() != null) {
            canonicalUrl += "?" + url.encodedQuery();
        }

        return method + "\n"
                + canonicalUrl + "\n"
                + timestamp + "\n"
                + nonceStr + "\n"
                + body + "\n";
    }

}
